﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
using System.Linq;
using System.Windows;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;
using IssueVision.Data.Web;
using IssueVision.Common;
using MVVMPlugin;

namespace IssueVision.ViewModel
{
    [ExportPlugin(ViewModelTypes.IssueEditorViewModel, PluginType.ViewModel)]
    [PartCreationPolicy(CreationPolicy.NonShared)]
    public class IssueEditorViewModel : ViewModelBase
    {
        #region "Private Data Members"
        private IIssueVisionModel _issueVisionModel;
        private Issue _currentIssueCache;
        #endregion "Private Data Members"

        #region "Constructor"
        public IssueEditorViewModel()
        {
            _issueVisionModel = PluginCatalogService.Container.GetExportedValue<IIssueVisionModel>();

            // set up event handling
            _issueVisionModel.GetIssueTypesComplete += _issueVisionModel_GetIssueTypesComplete;
            _issueVisionModel.GetPlatformsComplete += _issueVisionModel_GetPlatformsComplete;
            _issueVisionModel.GetResolutionsComplete += _issueVisionModel_GetResolutionsComplete;
            _issueVisionModel.GetStatusesComplete += _issueVisionModel_GetStatusesComplete;
            _issueVisionModel.GetSubStatusesComplete += _issueVisionModel_GetSubStatusesComplete;
            _issueVisionModel.GetUsersComplete += _issueVisionModel_GetUsersComplete;

            // set _currentIssueCache to null
            _currentIssueCache = null;

            // load issue type entries
            IssueTypeEntries = null;
            _issueVisionModel.GetIssueTypesAsync();
            // load platform entries
            PlatformEntries = null;
            _issueVisionModel.GetPlatformsAsync();
            //load resolution entries
            ResolutionEntriesWithNull = null;
            _issueVisionModel.GetResolutionsAsync();
            // load status entries
            StatusEntries = null;
            _issueVisionModel.GetStatusesAsync();
            // load substatus entries
            SubstatusEntriesWithNull = null;
            _issueVisionModel.GetSubStatusesAsync();
            // load user entries
            UserEntries = null;
            UserEntriesWithNull = null;
            _issueVisionModel.GetUsersAsync();

            // register for EditIssueMessage
            AppMessages.EditIssueMessage.Register(this, OnEditIssueMessage);
        }
        #endregion "Constructor"

        #region "ICleanup interface implementation"
        public override void Cleanup()
        {
            if (_issueVisionModel != null)
            {
                // unregister all events
                _issueVisionModel.GetIssueTypesComplete -= _issueVisionModel_GetIssueTypesComplete;
                _issueVisionModel.GetPlatformsComplete -= _issueVisionModel_GetPlatformsComplete;
                _issueVisionModel.GetResolutionsComplete -= _issueVisionModel_GetResolutionsComplete;
                _issueVisionModel.GetStatusesComplete -= _issueVisionModel_GetStatusesComplete;
                _issueVisionModel.GetSubStatusesComplete -= _issueVisionModel_GetSubStatusesComplete;
                _issueVisionModel.GetUsersComplete -= _issueVisionModel_GetUsersComplete;
                _issueVisionModel = null;
            }
            // set properties back to null
            CurrentIssue = null;
            IssueTypeEntries = null;
            PlatformEntries = null;
            ResolutionEntriesWithNull = null;
            StatusEntries = null;
            SubstatusEntriesWithNull = null;
            UserEntries = null;
            UserEntriesWithNull = null;
            // unregister any messages for this ViewModel
            base.Cleanup();
        }
        #endregion "ICleanup interface implementation"

        #region "Public Properties"

        private Issue _currentIssue;

        public Issue CurrentIssue
        {
            get { return _currentIssue; }
            private set
            {
                if (!ReferenceEquals(_currentIssue, value))
                {
                    _currentIssue = value;
                    RaisePropertyChanged("CurrentIssue");
                }
            }
        }

        private IEnumerable<byte> _priorityEntries;

        public IEnumerable<byte> PriorityEntries
        {
            get
            {
                if (_priorityEntries == null)
                {
                    var priorityList = new List<byte>();
                    for (var i = IssueVisionServiceConstant.HighestPriority; i <= IssueVisionServiceConstant.LowestPriority; i++)
                    {
                        priorityList.Add(i);
                    }
                    _priorityEntries = priorityList.AsEnumerable();
                }
                return _priorityEntries;
            }
        }

        private IEnumerable<byte> _severityEntries;

        public IEnumerable<byte> SeverityEntries
        {
            get
            {
                if (_severityEntries == null)
                {
                    var severityList = new List<byte>();
                    for (var i = IssueVisionServiceConstant.HighestSeverity; i <= IssueVisionServiceConstant.LowestSeverity; i++)
                    {
                        severityList.Add(i);
                    }
                    _severityEntries = severityList.AsEnumerable();
                }
                return _severityEntries;
            }
        }

        private IEnumerable<IssueType> _issueTypeEntries;

        public IEnumerable<IssueType> IssueTypeEntries
        {
            get { return _issueTypeEntries; }
            private set
            {
                if (!ReferenceEquals(_issueTypeEntries, value))
                {
                    _issueTypeEntries = value;
                    RaisePropertyChanged("IssueTypeEntries");
                }
            }
        }

        private IEnumerable<Platform> _platformEntries;

        public IEnumerable<Platform> PlatformEntries
        {
            get { return _platformEntries; }
            private set
            {
                if (!ReferenceEquals(_platformEntries, value))
                {
                    _platformEntries = value;
                    RaisePropertyChanged("PlatformEntries");
                }
            }
        }

        private IEnumerable<Resolution> _resolutionEntriesWithNull;

        public IEnumerable<Resolution> ResolutionEntriesWithNull
        {
            get { return _resolutionEntriesWithNull; }
            private set
            {
                if (!ReferenceEquals(_resolutionEntriesWithNull, value))
                {
                    _resolutionEntriesWithNull = value;
                    RaisePropertyChanged("ResolutionEntriesWithNull");
                }
            }
        }

        private IEnumerable<Status> _statusEntries;

        public IEnumerable<Status> StatusEntries
        {
            get { return _statusEntries; }
            private set
            {
                if (!ReferenceEquals(_statusEntries, value))
                {
                    _statusEntries = value;
                    RaisePropertyChanged("StatusEntries");
                }
            }
        }

        private IEnumerable<SubStatus> _substatusEntriesWithNull;

        public IEnumerable<SubStatus> SubstatusEntriesWithNull
        {
            get { return _substatusEntriesWithNull; }
            private set
            {
                if (!ReferenceEquals(_substatusEntriesWithNull, value))
                {
                    _substatusEntriesWithNull = value;
                    RaisePropertyChanged("SubstatusEntriesWithNull");
                }
            }
        }

        private IEnumerable<User> _userEntries;

        public IEnumerable<User> UserEntries
        {
            get { return _userEntries; }
            private set
            {
                if (!ReferenceEquals(_userEntries, value))
                {
                    _userEntries = value;
                    RaisePropertyChanged("UserEntries");
                }
            }
        }

        private IEnumerable<User> _userEntriesWithNull;

        public IEnumerable<User> UserEntriesWithNull
        {
            get { return _userEntriesWithNull; }
            private set
            {
                if (!ReferenceEquals(_userEntriesWithNull, value))
                {
                    _userEntriesWithNull = value;
                    RaisePropertyChanged("UserEntriesWithNull");
                }
            }
        }

        #endregion "Public Properties"

        #region "Public Commands"

        private RelayCommand<DragEventArgs> _handleDropCommand;

        public RelayCommand<DragEventArgs> HandleDropCommand
        {
            get
            {
                if (_handleDropCommand == null)
                {
                    _handleDropCommand = new RelayCommand<DragEventArgs>(
                        OnHandleDropCommand,
                        e => CurrentIssue != null);
                }
                return _handleDropCommand;
            }
        }

        private void OnHandleDropCommand(DragEventArgs e)
        {
            try
            {
                // get a list of files as FileInfo objects
                var files = e.Data.GetData(DataFormats.FileDrop) as FileInfo[];

                if (files != null)
                {
                    // loop through the list and read each file
                    foreach (var file in files)
                    {
                        using (var fs = file.OpenRead())
                        using (var ms = new MemoryStream())
                        {
                            fs.CopyTo(ms);
                            // and then add each file into the Files entity collection
                            CurrentIssue.Files.Add(
                                new Data.Web.File
                                {
                                    FileID = Guid.NewGuid(),
                                    IssueID = CurrentIssue.IssueID,
                                    FileName = file.Name,
                                    Data = ms.GetBuffer()
                                });
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                // notify user if there is any error
                AppMessages.RaiseErrorMessage.Send(ex);
            }
        }

        private RelayCommand _addFileCommand;

        public RelayCommand AddFileCommand
        {
            get
            {
                if (_addFileCommand == null)
                {
                    _addFileCommand = new RelayCommand(
                        OnAddFileCommand,
                        () => CurrentIssue != null);
                }
                return _addFileCommand;
            }
        }

        private void OnAddFileCommand()
        {
            try
            {
                if (!_issueVisionModel.IsBusy)
                {
                    FileInfo addedFile = null;
                    // ask user for a file to add
                    AppMessages.OpenFileMessage.Send(
                        new NotificationMessageAction<FileInfo>("Open File", g => addedFile = g));

                    if (addedFile != null)
                    {
                        using (var fs = addedFile.OpenRead())
                        using (var ms = new MemoryStream())
                        {
                            fs.CopyTo(ms);
                            // and then add the file into the Files entity collection
                            CurrentIssue.Files.Add(
                                new Data.Web.File
                                {
                                    FileID = Guid.NewGuid(),
                                    IssueID = CurrentIssue.IssueID,
                                    FileName = addedFile.Name,
                                    Data = ms.GetBuffer()
                                });
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                // notify user if there is any error
                AppMessages.RaiseErrorMessage.Send(ex);
            }
        }

        private RelayCommand<Data.Web.File> _removeFileCommand;

        public RelayCommand<Data.Web.File> RemoveFileCommand
        {
            get
            {
                if (_removeFileCommand == null)
                {
                    _removeFileCommand = new RelayCommand<Data.Web.File>(
                        OnRemoveFileCommand,
                        g => g != null);
                }
                return _removeFileCommand;
            }
        }

        private void OnRemoveFileCommand(Data.Web.File g)
        {
            try
            {
                if (!_issueVisionModel.IsBusy)
                {
                    CurrentIssue.Files.Remove(g);
                    _issueVisionModel.RemoveFile(g);
                }
            }
            catch (Exception ex)
            {
                // notify user if there is any error
                AppMessages.RaiseErrorMessage.Send(ex);
            }
        }

        private RelayCommand<Data.Web.File> _saveToDiskCommand;

        public RelayCommand<Data.Web.File> SaveToDiskCommand
        {
            get
            {
                if (_saveToDiskCommand == null)
                {
                    _saveToDiskCommand = new RelayCommand<Data.Web.File>(g =>
                    {
                        if (!_issueVisionModel.IsBusy)
                        {
                            AppMessages.SaveFileMessage.Send(g);
                        }
                    }, g => g != null);
                }
                return _saveToDiskCommand;
            }
        }

        private RelayCommand _addAttributeCommand;

        public RelayCommand AddAttributeCommand
        {
            get
            {
                if (_addAttributeCommand == null)
                {
                    _addAttributeCommand = new RelayCommand(
                        OnAddAttributeCommand,
                        () => CurrentIssue != null);
                }
                return _addAttributeCommand;
            }
        }

        private void OnAddAttributeCommand()
        {
            if (!_issueVisionModel.IsBusy)
            {
                CurrentIssue.Attributes.Add(
                    new Data.Web.Attribute
                    {
                        ID = Guid.NewGuid(),
                        IssueID = CurrentIssue.IssueID,
                        AttributeName = "Key",
                        Value = "Value"
                    });
            }
        }

        private RelayCommand<Data.Web.Attribute> _removeAttributeCommand;

        public RelayCommand<Data.Web.Attribute> RemoveAttributeCommand
        {
            get
            {
                if (_removeAttributeCommand == null)
                {
                    _removeAttributeCommand = new RelayCommand<Data.Web.Attribute>(g =>
                    {
                        if (!_issueVisionModel.IsBusy)
                        {
                            CurrentIssue.Attributes.Remove(g);
                            _issueVisionModel.RemoveAttribute(g);
                        }
                    }, g => g != null);
                }
                return _removeAttributeCommand;
            }
        }

        #endregion "Public Commands"

        #region "Private Methods"

        private void _issueVisionModel_GetIssueTypesComplete(object sender, EntityResultsArgs<IssueType> e)
        {
            if (!e.HasError)
            {
                IssueTypeEntries = e.Results.OrderBy(g => g.Name);
                // check whether IssueTypeEntries is populated after CurrentIssue
                AssignCurrentIssue(null);
            }
            else
            {
                // notify user if there is any error
                AppMessages.RaiseErrorMessage.Send(e.Error);
            }
        }

        private void _issueVisionModel_GetPlatformsComplete(object sender, EntityResultsArgs<Platform> e)
        {
            if (!e.HasError)
            {
                PlatformEntries = e.Results.OrderBy(g => g.OS).ThenBy(g => g.OSVersion);
                // check whether PlatformEntries is populated after CurrentIssue
                AssignCurrentIssue(null);
            }
            else
            {
                // notify user if there is any error
                AppMessages.RaiseErrorMessage.Send(e.Error);
            }
        }

        private void _issueVisionModel_GetResolutionsComplete(object sender, EntityResultsArgs<Resolution> e)
        {
            if (!e.HasError)
            {
                // create a new resolution list with the first item of null Name
                var resolutionList = e.Results.ToList();
                resolutionList.Add(new Resolution());
                ResolutionEntriesWithNull = resolutionList.OrderBy(g => g.Name).AsEnumerable();
                // check whether ResolutionEntriesWithNull is populated after CurrentIssue
                AssignCurrentIssue(null);
            }
            else
            {
                // notify user if there is any error
                AppMessages.RaiseErrorMessage.Send(e.Error);
            }
        }

        private void _issueVisionModel_GetStatusesComplete(object sender, EntityResultsArgs<Status> e)
        {
            if (!e.HasError)
            {
                StatusEntries = e.Results.OrderBy(g => g.StatusID);
                // check whether StatusEntries is populated after CurrentIssue
                AssignCurrentIssue(null);
            }
            else
            {
                // notify user if there is any error
                AppMessages.RaiseErrorMessage.Send(e.Error);
            }
        }

        private void _issueVisionModel_GetSubStatusesComplete(object sender, EntityResultsArgs<SubStatus> e)
        {
            if (!e.HasError)
            {
                // create a new substatus list with the first item of null Name
                var substatusList = e.Results.ToList();
                substatusList.Add(new SubStatus());
                SubstatusEntriesWithNull = substatusList.OrderBy(g => g.Name).AsEnumerable();
                // check whether SubstatusEntriesWithNull is populated after CurrentIssue
                AssignCurrentIssue(null);
            }
            else
            {
                // notify user if there is any error
                AppMessages.RaiseErrorMessage.Send(e.Error);
            }
        }

        private void _issueVisionModel_GetUsersComplete(object sender, EntityResultsArgs<User> e)
        {
            if (!e.HasError)
            {
                UserEntries = e.Results.OrderBy(g => g.Name);
                // create a new user list with the first item of null user name
                var userList = e.Results.ToList();
                userList.Add(new User());
                UserEntriesWithNull = userList.OrderBy(g => g.Name).AsEnumerable();
                // check whether UserEntries/UserEntriesWithNull is populated after CurrentIssue
                AssignCurrentIssue(null);
            }
            else
            {
                // notify user if there is any error
                AppMessages.RaiseErrorMessage.Send(e.Error);
            }
        }

        private void OnEditIssueMessage(Issue editIssue)
        {
            if (editIssue == null) return;
            // check whether this issue is read-only or not
            AppMessages.ReadOnlyIssueMessage.Send(editIssue.IsIssueReadOnly());

            AssignCurrentIssue(editIssue);
        }

        /// <summary>
        /// Assign edit issue only after all ComboBox are ready
        /// </summary>
        /// <param name="editIssue"></param>
        private void AssignCurrentIssue(Issue editIssue)
        {
            if (editIssue != null)
            {
                // this call is coming from OnEditIssueMessage()
                if (IssueTypeEntries != null && PlatformEntries != null &&
                    ResolutionEntriesWithNull != null && StatusEntries != null &&
                    SubstatusEntriesWithNull != null && UserEntries != null &&
                    UserEntriesWithNull != null)
                {
                    // if all ComboBox are ready, we set CurrentIssue
                    CurrentIssue = editIssue;
                    _currentIssueCache = null;
                }
                else
                    _currentIssueCache = editIssue;
            }
            else
            {
                // this call is coming from one of the complete event handlers
                if (_currentIssueCache != null && IssueTypeEntries != null &&
                    PlatformEntries != null && ResolutionEntriesWithNull != null &&
                    StatusEntries != null && SubstatusEntriesWithNull != null &&
                    UserEntries != null && UserEntriesWithNull != null)
                {
                    // if all ComboBox are ready, we set CurrentIssue
                    CurrentIssue = _currentIssueCache;
                    _currentIssueCache = null;
                }
            }
        }

        #endregion "Private Methods"
    }
}